// Virtual Methods (虚方法) 示例
// 演示 Pascal 中虚方法的各种用法
program p_30_virtual_methods;

{$mode objfpc}{$H+}
// {$mode objfpc} 启用 Object Pascal 模式，支持面向对象编程
// {$H+} 启用长字符串支持（AnsiString）

// 引入单元文件
uses
  SysUtils;  // 系统工具单元，包含常用的系统函数

// 定义基类：形状
type
  TShape = class
  protected
    FName: String;
  public
    constructor Create(const AName: String);
    // 虚方法：计算面积
    function GetArea: Real; virtual;
    // 虚方法：显示信息
    procedure DisplayInfo; virtual;
    // 虚方法：绘制形状
    procedure Draw; virtual;
    // 析构函数也是虚函数
    destructor Destroy; override;
    property Name: String read FName;
  end;

// TShape类的实现
constructor TShape.Create(const AName: String);
begin
  inherited Create;
  FName := AName;
  WriteLn('  创建形状: ', FName);
end;

function TShape.GetArea: Real;
begin
  Result := 0;  // 基类不实现具体计算
  WriteLn('  计算 ', FName, ' 的面积');
end;

procedure TShape.DisplayInfo;
begin
  WriteLn('  形状名称: ', FName);
  WriteLn('  面积: ', GetArea:0:2);
end;

procedure TShape.Draw;
begin
  WriteLn('  绘制基本形状');
end;

destructor TShape.Destroy;
begin
  WriteLn('  销毁形状: ', FName);
  inherited Destroy;
end;

// 定义派生类：矩形
type
  TRectangle = class(TShape)
  private
    FWidth, FHeight: Real;
  public
    constructor Create(const AName: String; AWidth, AHeight: Real);
    // 重写虚方法：计算面积
    function GetArea: Real; override;
    // 重写虚方法：显示信息
    procedure DisplayInfo; override;
    // 重写虚方法：绘制形状
    procedure Draw; override;
    property Width: Real read FWidth;
    property Height: Real read FHeight;
  end;

// TRectangle类的实现
constructor TRectangle.Create(const AName: String; AWidth, AHeight: Real);
begin
  inherited Create(AName);
  FWidth := AWidth;
  FHeight := AHeight;
  WriteLn('  创建矩形: ', FName, ', 宽度: ', FWidth:0:1, ', 高度: ', FHeight:0:1);
end;

function TRectangle.GetArea: Real;
begin
  Result := FWidth * FHeight;
  WriteLn('  计算矩形 ', FName, ' 的面积: ', FWidth:0:1, ' * ', FHeight:0:1, ' = ', Result:0:2);
end;

procedure TRectangle.DisplayInfo;
begin
  WriteLn('  矩形信息:');
  WriteLn('    名称: ', FName);
  WriteLn('    宽度: ', FWidth:0:1);
  WriteLn('    高度: ', FHeight:0:1);
  WriteLn('    面积: ', GetArea:0:2);
end;

procedure TRectangle.Draw;
begin
  WriteLn('  绘制矩形: ', FName);
  WriteLn('    使用坐标绘制一个 ', FWidth:0:1, 'x', FHeight:0:1, ' 的矩形');
end;

// 定义派生类：圆形
type
  TCircle = class(TShape)
  private
    FRadius: Real;
  public
    constructor Create(const AName: String; ARadius: Real);
    // 重写虚方法：计算面积
    function GetArea: Real; override;
    // 重写虚方法：显示信息
    procedure DisplayInfo; override;
    // 重写虚方法：绘制形状
    procedure Draw; override;
    property Radius: Real read FRadius;
  end;

// TCircle类的实现
constructor TCircle.Create(const AName: String; ARadius: Real);
begin
  inherited Create(AName);
  FRadius := ARadius;
  WriteLn('  创建圆形: ', FName, ', 半径: ', FRadius:0:1);
end;

function TCircle.GetArea: Real;
begin
  Result := 3.14159 * FRadius * FRadius;
  WriteLn('  计算圆形 ', FName, ' 的面积: π * ', FRadius:0:1, '² = ', Result:0:2);
end;

procedure TCircle.DisplayInfo;
begin
  WriteLn('  圆形信息:');
  WriteLn('    名称: ', FName);
  WriteLn('    半径: ', FRadius:0:1);
  WriteLn('    面积: ', GetArea:0:2);
end;

procedure TCircle.Draw;
begin
  WriteLn('  绘制圆形: ', FName);
  WriteLn('    以半径 ', FRadius:0:1, ' 绘制圆形');
end;

// 定义派生类：三角形
type
  TTriangle = class(TShape)
  private
    FBase, FHeight: Real;
  public
    constructor Create(const AName: String; ABase, AHeight: Real);
    // 重写虚方法：计算面积
    function GetArea: Real; override;
    // 重写虚方法：显示信息
    procedure DisplayInfo; override;
    // 重写虚方法：绘制形状
    procedure Draw; override;
    property Base: Real read FBase;
    property Height: Real read FHeight;
  end;

// TTriangle类的实现
constructor TTriangle.Create(const AName: String; ABase, AHeight: Real);
begin
  inherited Create(AName);
  FBase := ABase;
  FHeight := AHeight;
  WriteLn('  创建三角形: ', FName, ', 底边: ', FBase:0:1, ', 高度: ', FHeight:0:1);
end;

function TTriangle.GetArea: Real;
begin
  Result := 0.5 * FBase * FHeight;
  WriteLn('  计算三角形 ', FName, ' 的面积: 0.5 * ', FBase:0:1, ' * ', FHeight:0:1, ' = ', Result:0:2);
end;

procedure TTriangle.DisplayInfo;
begin
  WriteLn('  三角形信息:');
  WriteLn('    名称: ', FName);
  WriteLn('    底边: ', FBase:0:1);
  WriteLn('    高度: ', FHeight:0:1);
  WriteLn('    面积: ', GetArea:0:2);
end;

procedure TTriangle.Draw;
begin
  WriteLn('  绘制三角形: ', FName);
  WriteLn('    绘制底边为 ', FBase:0:1, '，高为 ', FHeight:0:1, ' 的三角形');
end;

// 程序主入口
begin
  WriteLn('=== Pascal 虚方法示例 ===');
  WriteLn;
  
  // 1. 创建各种形状对象
  WriteLn('1. 创建各种形状对象:');
  var rect: TRectangle := TRectangle.Create('我的矩形', 5.0, 3.0);
  var circle: TCircle := TCircle.Create('我的圆形', 4.0);
  var triangle: TTriangle := TTriangle.Create('我的三角形', 6.0, 4.0);
  
  try
    WriteLn;
    
    // 2. 调用重写的虚方法
    WriteLn('2. 调用重写的虚方法:');
    rect.DisplayInfo;
    WriteLn;
    circle.DisplayInfo;
    WriteLn;
    triangle.DisplayInfo;
    
    WriteLn;
    
    // 3. 多态性演示
    WriteLn('3. 多态性演示:');
    var shapes: array[1..3] of TShape;
    
    shapes[1] := rect;
    shapes[2] := circle;
    shapes[3] := triangle;
    
    for var i := 1 to 3 do
    begin
      WriteLn('  处理形状 ', i, ':');
      shapes[i].Draw;        // 调用重写的Draw方法
      shapes[i].DisplayInfo; // 调用重写的DisplayInfo方法
      WriteLn;
    end;
    
    WriteLn;
    
    // 4. 直接调用虚方法
    WriteLn('4. 直接调用虚方法:');
    WriteLn('  矩形面积: ', rect.GetArea:0:2);
    WriteLn('  圆形面积: ', circle.GetArea:0:2);
    WriteLn('  三角形面积: ', triangle.GetArea:0:2);
    
    WriteLn;
    
    // 5. 通过基类引用调用虚方法（多态）
    WriteLn('5. 通过基类引用调用虚方法（多态）:');
    var shape: TShape;
    
    shape := rect;
    WriteLn('  通过基类引用调用矩形方法:');
    shape.Draw;
    WriteLn('  面积: ', shape.GetArea:0:2);
    
    WriteLn;
    
    shape := circle;
    WriteLn('  通过基类引用调用圆形方法:');
    shape.Draw;
    WriteLn('  面积: ', shape.GetArea:0:2);
    
    WriteLn;
    
    shape := triangle;
    WriteLn('  通过基类引用调用三角形方法:');
    shape.Draw;
    WriteLn('  面积: ', shape.GetArea:0:2);
  finally
    // 释放对象
    rect.Free;
    circle.Free;
    triangle.Free;
  end;
  
  WriteLn;
  
  // 6. 虚析构函数演示
  WriteLn('6. 虚析构函数演示:');
  WriteLn('  创建临时对象:');
  var tempShape: TShape := TRectangle.Create('临时矩形', 2.0, 2.0);
  WriteLn('  释放临时对象:');
  tempShape.Free;
  
  WriteLn;
  WriteLn('演示了 Pascal 中虚方法的各种用法');
  
  // 等待用户按键，防止程序窗口立即关闭
  ReadLn;
end.